package com.manager.commons.encrypt.rsa;

//			 	     _ooOoo_    
//				    o8888888o    
//				    88" . "88    
//				    (| -_- |)    
//			 	     O\ = /O    
//		         ____/'---'\____    
//	   	        /. '\\| : |//' .\    
//		       // \\||| : |||// \\   
//	          // _|||||-:-|||||_ \\    
//           // | | \\\ - /// | | \\
//           || |_| ''\---/'' |_| || 
//            \ .-\___/'-'\___/-. /    
//          ___\. .' /--.--\ '. ./___    
//        .""'< `.___\_<|>_/___.' >'"".    
//       | |: `- \`.|'\ _ /'|.`/ -` :| |    
//    	  \ \ `-. \_ __\ /__ _/ .-` / /    
//======'-.___`-.___\_______/___.-`___.-'======    
//				     '=---='    
//.................................................    
//写一哈代码，你就青叫唤；信龟儿设计，你逗是傻逼。
//永无Bug，啷个可能！求佛主保佑也没球用！

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;

import com.manager.commons.log.LogFactory;

/**
 * RSAUtils
 * @author H
 * @version V1.0
 */
public class RSAUtils {

	private static final String STR_PUBLICKEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3jg5eFRbBNeIg20xM6Ukn8Mu4WmvqxdSbmjg74T4o52zfUP/RZfIwx+7yt3aaEuxqkgTagaV1oR91uR6IQqmk++05gHzgigIK8G+eX9fGqnZWDaugH5HUteTyY8SjvCRY9gxuaP6jNp9r+Ngk9EC4m8qlZY4cRx3XySlEPjgJinvtzWTOvszG84N87r3RBKl/989itTFr/lLXdERawme6C5/wTQXpvYB8WHY8hnK/IkZ2ynS1eCFSCHoLyouUr/dCT+wPEFotGtK7mjle6U1MxVzehZqsT119M3diSDXNoOxF18GNfYS68RnvDhDuIUFFtkSGjHqoxKFjpbArjNykQIDAQAB";
	private static final String STR_PRIVATEKEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeODl4VFsE14iDbTEzpSSfwy7haa+rF1JuaODvhPijnbN9Q/9Fl8jDH7vK3dpoS7GqSBNqBpXWhH3W5HohCqaT77TmAfOCKAgrwb55f18aqdlYNq6AfkdS15PJjxKO8JFj2DG5o/qM2n2v42CT0QLibyqVljhxHHdfJKUQ+OAmKe+3NZM6+zMbzg3zuvdEEqX/3z2K1MWv+Utd0RFrCZ7oLn/BNBem9gHxYdjyGcr8iRnbKdLV4IVIIegvKi5Sv90JP7A8QWi0a0ruaOV7pTUzFXN6FmqxPXX0zd2JINc2g7EXXwY19hLrxGe8OEO4hQUW2RIaMeqjEoWOlsCuM3KRAgMBAAECggEAZ1Dwt1dQ8hFbcO9lfsN7IaRXuYmvmPZA5wBwBCxohXmep9DjcHG/b6jjYNCA6Ri4Q5k7HYJuS8xRzYVwmsge5oz+KCokExGVqvJrIoXrq7PoTzEtNKSBeKyuE3ongLmsfyVXCR6KMM6svQgUMOVuV+SjgeTT3RMFFQEulZOAc30R752aJSc6g4uqQUf1goqlfpMpJ8W3iOWHJ/T4elI0xmlbDffWr1mV8DJSKrWD/Ynnd1EYhllaKcNV7UUWtu1hPgbOKz0pCtOFYeRqHG45xkm0vaoFg+neV2pS//tqjah5tvcPoldNVTXRkwvRz78jmhLob5ndffNHx+kZC4a4OQKBgQD470aPAe5jytdNUsasmDWMPDdOzBt6AEDGky0PpyoT1u2XEMFlk9S/tM2ywYWIbGacm4Qjt5zYSKXNI9ypf04ol9ZvaXXVRNoE6ad/QBUxCmI0pvdMYwebfMnJYCXcQrcmZIKCnWbHwubwpKM8dexFkYPNyzZMYjZFPgW10p8x+wKBgQDkhtdTYJxMBlJVLgRh+TVeYRCac4Vt6Okha6ZfXbgyfc1wvEDZ59eQoFruNX0IJT/E56jpYVyGEmjeS3MgAV356c6ks9lV5t3yG3VlwUxQRi1/lyCAzsL7rankLwr9S7j85j1dJ526/7QwfmP41GRTsy9wJPR7/iTx7v+ia1WT4wKBgGGu2zxA4MAOF8CC6+1MaS7XbkigdWPBd6m0lSkgSrWwUFlIPOvUP9beZx1vLSwkhXuM1ySA565Di7RdDzj/+LabJo1fj9qZgwgMfCrGBcrRrUnF/yLddV3BcRlxfknZcC9Dn6cLhwp6Y88oe9m82HNQL61wwaTloTA4r1rzBH9DAoGBAJvuXhKoQz2kup0gjyEC2KWTea7J0GuUly6Us6sy2bKqIHoiGeYVygMHR8RufugF8qM6pwjHxEg/W4sC1IUsKaGTJctAZLW2mA7RKaPK53WGzRjYV/dMHcN5Vgk7CDaZapo1x/0+QOQOAjdspm5tDiAGjgHpVWEdorvqJkf/ER+dAoGAFrumnpZtR20UlcNeQ6Rt0NZHhpBFtBqO0xpm+L3ZqneW0WLr7wTpWfEZu45XMdBmX6ftIF8gNtub+Po5/dHn8cML/+n8asl99E6kxrr+hZ6+Pge3X382x2BK6Et3nJIEjd2wGut+Ifu+gOhWYWAClX6mASfN6BRnywo5vM/BGdA=";

	private static final String KEY_ALGORITHM = "RSA";
	private static final int KEY_KEYSIZE = 1024;

	private static final BouncyCastleProvider bcProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
	private static final Logger logger = LogFactory.getLogger(RSAUtils.class);

    /**
	 * 生成RSA密钥对
	 * @return
	 */
	public static String[] generateRSAKeys() {
		RSAKeyPair rsaKeyPair = generateRSAKeyPair();
		if (null != rsaKeyPair) {
			RSAPublicKey rsaPublicKey = rsaKeyPair.getPublicKey();
			RSAPrivateKey rsaPrivateKey = rsaKeyPair.getPrivateKey();
			if (null != rsaPublicKey && null != rsaPrivateKey) {
				String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
				String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
				return new String[] { publicKeyString, privateKeyString };
			}
			return new String[] { STR_PUBLICKEY, STR_PRIVATEKEY };
		}
		return new String[] { STR_PUBLICKEY, STR_PRIVATEKEY };
	}

	/**
	 * 生成RSA密钥对
	 * @return 生成后的公私钥信息
	 */
	public static RSAKeyPair generateRSAKeyPair() {
		return generateRSAKeyPair(0);
	}

	/**
	 * 生成RSA密钥对
	 * @return
	 */
	public static RSAKeyPair generateRSAKeyPair(int random) {
		KeyPairGenerator keyPairGenerator;
		try {
			keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
			if (random > 0) {
				keyPairGenerator.initialize(KEY_KEYSIZE, new SecureRandom());
			} else {
				keyPairGenerator.initialize(KEY_KEYSIZE);
			}
			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
			return new RSAKeyPair(rsaPublicKey, rsaPrivateKey);
		} catch (NoSuchAlgorithmException e) {
			logger.error(e.getMessage(), e);
		}
		return new RSAKeyPair();
	}

	/**
	 * 公钥加密
	 * @param pks  公钥
	 * @param text 待加密的文本
	 * @return 加密后的文本
	 */
	public static String encryptByPublicKey(String pks, String text) throws Exception {
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pks));
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		byte[] result = cipher.doFinal(text.getBytes());
		return Base64.encodeBase64String(result);
	}

	/**
	 * 私钥加密
	 * @param pks  私钥
	 * @param text 待加密的信息
	 * @return 加密后的文本
	 */
	public static String encryptByPrivateKey(String pks, String text) throws Exception {
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(pks));
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		byte[] result = cipher.doFinal(text.getBytes());
		return Base64.encodeBase64String(result);
	}

	/**
	 * 公钥解密
	 * @param pks  公钥
	 * @param text 待解密的信息
	 * @return 解密后的文本
	 */
	public static String decryptByPublicKey(String pks, String text) throws Exception {
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pks));
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		byte[] result = cipher.doFinal(Base64.decodeBase64(text));
		return new String(result);
	}

	/**
	 * 私钥解密
	 * @param pks  私钥
	 * @param text 待解密文本
	 * @return 解密后的文本
	 */
	public static String decryptByPrivateKey(String pks, String text) throws Exception {
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(pks));
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		byte[] result = cipher.doFinal(Base64.decodeBase64(text));
		return new String(result);
	}

	/**
	 * 生成公钥和密钥
	 * @return
	 */
	public static RSAKeyPair getBCRSAKeyPair() {
		Security.addProvider(bcProvider);
		KeyPairGenerator keyPairGen;
		try {
			keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM, bcProvider);
			keyPairGen.initialize(KEY_KEYSIZE);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			return new RSAKeyPair(publicKey, privateKey);
		} catch (NoSuchAlgorithmException e) {
			logger.error(e.getMessage(), e);
		}
		return new RSAKeyPair();
	}

	/**
	 * 使用模和指数生成RSA公钥
	 * @param modulus  模
	 * @param exponent 指数
	 * @return
	 */
	public static RSAPublicKey getMEPublicKey(String modulus, String exponent) {
		Security.addProvider(bcProvider);
		try {
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bcProvider);
			RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 使用模和指数生成RSA私钥
	 * @param modulus  模
	 * @param exponent 指数
	 * @return
	 */
	public static RSAPrivateKey getMEPrivateKey(String modulus, String exponent) {
		try {
			Security.addProvider(bcProvider);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bcProvider);
			RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(exponent));
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 公钥加密
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptJs(String data, RSAPublicKey publicKey) {
		try {
			Security.addProvider(bcProvider);
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM, bcProvider);
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			// 加密数据长度 <= 模长-11
			String[] datas = splitString(data, ((publicKey.getModulus().bitLength() / 8) - 11));
			String str = "";
			// 如果明文长度大于模长-11则要分组加密
			for (String s : datas) {
				str += bcd2Str(cipher.doFinal(s.getBytes()));
			}
			return str;
		} catch (Exception e) {

		}
		return "";
	}

	/**
	 * 私钥解密
	 * @param modulus
	 * @param exponent
	 * @param data
	 * @return
	 */
	public static String decryptJs(String modulus, String exponent, String data) {
		return decryptJs(data, getMEPrivateKey(modulus, exponent));
	}

	/**
	 * 私钥解密
	 * @param data
	 * @param privateKey
	 * @param reverse
	 * @return
	 */
	public static String decryptJs(String data, RSAPrivateKey privateKey, int reverse) {
		try {
			Security.addProvider(bcProvider);
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM, bcProvider);
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] bytes = data.getBytes();
			byte[] bcd = asciiToBcd(bytes, bytes.length);
			// 如果密文长度大于模长则要分组解密
			String str = "";
			byte[][] arrs = splitArray(bcd, (privateKey.getModulus().bitLength() / 8));
			for (byte[] arr : arrs) {
				str += new String(cipher.doFinal(arr));
			}
			StringBuilder sb = new StringBuilder(str);
			if (reverse > 0) {
				sb.reverse();
			}
			return sb.toString();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return "";
	}

	/**
	 * 私钥解密
	 * @param data
	 * @param privateKey
	 * @return
	 */
	public static String decryptJs(String data, RSAPrivateKey privateKey) {
		return decryptJs(data, privateKey, 1);
	}

	/**
	 * ASCII码转BCD码
	 * @param ascii
	 * @param ascLen
	 * @return
	 */
	public static byte[] asciiToBcd(byte[] ascii, int ascLen) {
		byte[] bcd = new byte[ascLen / 2];
		int j = 0;
		for (int i = 0; i < (ascLen + 1) / 2; i++) {
			bcd[i] = ascToBcd(ascii[j++]);
			bcd[i] = (byte) (((j >= ascLen) ? 0x00 : ascToBcd(ascii[j++])) + (bcd[i] << 4));
		}
		return bcd;
	}

	/**
	 * ASC转BCD码
	 * @param asc
	 * @return
	 */
	public static byte ascToBcd(byte asc) {
		byte bcd;

		if ((asc >= '0') && (asc <= '9'))
			bcd = (byte) (asc - '0');
		else if ((asc >= 'A') && (asc <= 'F'))
			bcd = (byte) (asc - 'A' + 10);
		else if ((asc >= 'a') && (asc <= 'f'))
			bcd = (byte) (asc - 'a' + 10);
		else
			bcd = (byte) (asc - 48);
		return bcd;
	}

	/**
	 * BCD转字符串
	 * @param bytes
	 * @return
	 */
	public static String bcd2Str(byte[] bytes) {
		char[] temp = new char[bytes.length * 2];
        char val;

        for (int i = 0; i < bytes.length; i++) {
			val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
			temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

			val = (char) (bytes[i] & 0x0f);
			temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
		}
		return new String(temp);
	}

	/**
	 * 拆分字符串
	 * @param string
	 * @param len
	 * @return
	 */
	public static String[] splitString(String string, int len) {
		int x = string.length() / len;
		int y = string.length() % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		String[] strs = new String[x + z];
		String str = "";
		for (int i = 0; i < x + z; i++) {
			if (i == x + z - 1 && y != 0) {
				str = string.substring(i * len, i * len + y);
			} else {
				str = string.substring(i * len, i * len + len);
			}
			strs[i] = str;
		}
		return strs;
	}

	/**
	 * 拆分数组
	 * @param data
	 * @param len
	 * @return
	 */
	public static byte[][] splitArray(byte[] data, int len) {
		int x = data.length / len;
		int y = data.length % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		byte[][] arrs = new byte[x + z][];
		byte[] arr;
		for (int i = 0; i < x + z; i++) {
			arr = new byte[len];
			if (i == x + z - 1 && y != 0) {
				System.arraycopy(data, i * len, arr, 0, y);
			} else {
				System.arraycopy(data, i * len, arr, 0, len);
			}
			arrs[i] = arr;
		}
		return arrs;
	}
}